﻿// Decompiled with JetBrains decompiler
// Type: Netick.ClientSimulation
// Assembly: Netick, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 6726ECA1-C773-4CF7-8952-E81B30D93B52
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.xml

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

#nullable disable
namespace Netick;

public sealed class ClientSimulation : Simulation
{
  internal int LocalInputSourceIndex = -1;
  internal Tick CurrentTick = Tick.InvalidTick;
  internal Tick PredictedTick = Tick.InvalidTick;
  internal Tick AuthoritativeTick = Tick.InvalidTick;
  internal Tick LastAuthoritativeTick = Tick.InvalidTick;
  internal bool IsLastTickFullPrevious;
  internal NetickAllocator[] _snapshots;
  internal NetickAllocator _recvBuffer;
  internal NetickAllocator _recvMap;
  internal NetickAllocator _lastAuth;
  internal NetickAllocator _preRollback;
  internal NetickAllocator _interpFrom;
  internal ExecutionList PredictedEntities;
  internal List<Entity> ChangedEntities;
  internal HashSet<Entity> ReceivedEntities = new HashSet<Entity>(512 /*0x0200*/);
  internal HashSet<Entity> ReceivedEntitiesNarrow = new HashSet<Entity>(512 /*0x0200*/);
  internal HashSet<Entity> ClientPendingAddedEntities;
  internal Queue<Simulation.InputSourceChangedEvent> PendingClientInputSourceChangedEvents;
  internal HashSet<Entity> InputSourceChangedEntities;
  public HashSet<InterestManagementGroup> InterestGroups = new HashSet<InterestManagementGroup>(512 /*0x0200*/);
  internal HashSet<InterestManagementGroup> ReceivedGroups = new HashSet<InterestManagementGroup>(512 /*0x0200*/);
  private HashSet<InterestManagementGroup> _preInterestList = new HashSet<InterestManagementGroup>(512 /*0x0200*/);
  internal List<SimulationInput> SavedInputs = new List<SimulationInput>(512 /*0x0200*/);
  internal double InputReceiveDeltaStd;
  internal double InputReceiveDeltaAvg;
  internal bool SimulationReady;
  internal ClientReplicatorData[] _clientReplicatorDataBuffer;

  public bool IsLastTickFull { get; internal set; }

  public bool IsResimulating { get; internal set; }

  public int ResimulationCount { get; internal set; }

  public int ResimulationStep { get; internal set; }

  internal ClientSimulation(NetickEngine engine)
    : base(engine)
  {
    this._recvBuffer = new NetickAllocator((long) engine._config.StateAllocatorBlockSize);
    this._recvMap = new NetickAllocator((long) engine._config.StateAllocatorBlockSize);
    this._preRollback = new NetickAllocator((long) engine._config.StateAllocatorBlockSize);
    this._interpFrom = new NetickAllocator((long) engine._config.StateAllocatorBlockSize);
    this.StateAllocator.Link(this._interpFrom);
    this.StateAllocator.Link(this._preRollback);
    this.StateAllocator.Link(this._recvBuffer);
    this.StateAllocator.Link(this._recvMap);
    if (this.Engine._config.AccurateLocalInterpolation)
      this.From = this._interpFrom;
    else
      this.From = this.StateAllocator._allocator;
    this.To = this.StateAllocator._allocator;
    this._snapshots = new NetickAllocator[engine._config.MaxSnapshots];
    for (int index = 0; index < this._snapshots.Length; ++index)
    {
      this._snapshots[index] = new NetickAllocator((long) engine._config.StateAllocatorBlockSize);
      this.StateAllocator.Link(this._snapshots[index]);
    }
    this.PredictedEntities = new ExecutionList(this.Engine);
    this.PendingClientInputSourceChangedEvents = new Queue<Simulation.InputSourceChangedEvent>(512 /*0x0200*/);
    this.InputSourceChangedEntities = new HashSet<Entity>(512 /*0x0200*/);
    this.ChangedEntities = new List<Entity>(this.Engine.Config.MaxObjects);
    this.ClientPendingAddedEntities = new HashSet<Entity>(this.Engine.Config.MaxObjects);
    this._clientReplicatorDataBuffer = new ClientReplicatorData[engine._config.MaxSnapshots];
  }

  internal override void Reset()
  {
    this.LocalInputSourceIndex = -1;
    this.CurrentTick = Tick.InvalidTick;
    this.PredictedTick = Tick.InvalidTick;
    this.AuthoritativeTick = Tick.InvalidTick;
    this.LastAuthoritativeTick = Tick.InvalidTick;
    this.ReceivedGroups.Clear();
    this._lastAuth = (NetickAllocator) null;
    this.IsLastTickFull = false;
    this.IsLastTickFullPrevious = false;
    this.ResetSimulationData();
  }

  internal void ResetSimulationData()
  {
    this.SimulationReady = false;
    this.Engine._timer.Reset();
    this.Engine.Client.ConnectedServer.RTT.ResetTo((double) Math.Max(this.Engine.FixedDeltaTime * (float) this.Engine.Config.ServerDivisor, 0.05f));
  }

  internal void RemoveEntity(Entity entity)
  {
    entity._isOnPredictedList = false;
    this.PredictedEntities.Remove(entity);
    this.ReceivedEntitiesNarrow.Remove(entity);
    this.ReceivedEntities.Remove(entity);
    this.ChangedEntities.RemoveUnordered(entity);
    this.ClientPendingAddedEntities.Remove(entity);
  }

  internal unsafe void InitUnlinkedEntity(Entity entity)
  {
    int blockIndex = entity.BlockIndex;
    long stateOffsetBytes = entity.StateOffsetBytes;
    long sizeBytes = entity.StateSizeWords * 4L;
    MemoryAllocation.Clear((void*) ((IntPtr) this._recvMap.Blocks[blockIndex].Ptr + (IntPtr) stateOffsetBytes), sizeBytes);
    MemoryAllocation.Clear((void*) ((IntPtr) this._recvBuffer.Blocks[blockIndex].Ptr + (IntPtr) stateOffsetBytes), sizeBytes);
    for (int index = 0; index < this._snapshots.Length; ++index)
      MemoryAllocation.Clear((void*) ((IntPtr) this._snapshots[index].Blocks[blockIndex].Ptr + (IntPtr) stateOffsetBytes), sizeBytes);
  }

  internal override unsafe void InitEntity(Entity entity, NetickAllocator source, bool unlinked)
  {
    int blockIndex = entity.BlockIndex;
    long stateOffsetBytes = entity.StateOffsetBytes;
    long sizeBytes = entity.StateSizeWords * 4L;
    long stateSizeWords = entity.StateSizeWords;
    int* numPtr1 = (int*) ((IntPtr) this._recvMap.Blocks[blockIndex].Ptr + (IntPtr) entity.StateOffsetBytes);
    int* numPtr2 = (int*) ((IntPtr) this._recvBuffer.Blocks[blockIndex].Ptr + (IntPtr) entity.StateOffsetBytes);
    int* numPtr3 = (int*) ((IntPtr) source.Blocks[blockIndex].Ptr + (IntPtr) entity.StateOffsetBytes);
    if (unlinked)
    {
      for (int index = 0; (long) index < stateSizeWords; ++index)
      {
        if (numPtr1[index] == 0)
          numPtr2[index] = numPtr3[index];
      }
    }
    else
    {
      MemoryAllocation.Copy((void*) ((IntPtr) this._recvBuffer.Blocks[blockIndex].Ptr + (IntPtr) stateOffsetBytes), (void*) ((IntPtr) source.Blocks[blockIndex].Ptr + (IntPtr) stateOffsetBytes), sizeBytes);
      for (int index = 0; index < this._snapshots.Length; ++index)
        MemoryAllocation.Clear((void*) ((IntPtr) this._snapshots[index].Blocks[blockIndex].Ptr + (IntPtr) stateOffsetBytes), sizeBytes);
    }
    MemoryAllocation.Copy((void*) ((IntPtr) this.Previous.Blocks[blockIndex].Ptr + (IntPtr) stateOffsetBytes), (void*) ((IntPtr) source.Blocks[blockIndex].Ptr + (IntPtr) stateOffsetBytes), sizeBytes);
  }

  internal unsafe void InitEntityInterpolationBuffers(Entity entity, NetickAllocator source)
  {
    int blockIndex = entity.BlockIndex;
    long stateOffsetBytes = entity.StateOffsetBytes;
    long sizeBytes = entity.StateSizeWords * 4L;
    foreach (NetickAllocator snapshot in this.Engine.RemoteInterpolation._snapshots)
      MemoryAllocation.Copy((void*) ((IntPtr) snapshot.Blocks[blockIndex].Ptr + (IntPtr) stateOffsetBytes), (void*) ((IntPtr) source.Blocks[blockIndex].Ptr + (IntPtr) stateOffsetBytes), sizeBytes);
    if (this.Engine._config.AccurateLocalInterpolation)
      MemoryAllocation.Copy((void*) ((IntPtr) this._interpFrom.Blocks[blockIndex].Ptr + (IntPtr) stateOffsetBytes), (void*) ((IntPtr) source.Blocks[blockIndex].Ptr + (IntPtr) stateOffsetBytes), sizeBytes);
    else
      MemoryAllocation.Copy((void*) ((IntPtr) this._preRollback.Blocks[blockIndex].Ptr + (IntPtr) stateOffsetBytes), (void*) ((IntPtr) source.Blocks[blockIndex].Ptr + (IntPtr) stateOffsetBytes), sizeBytes);
  }

  internal override void Step()
  {
    this.CurrentInput = (SimulationInput) null;
    if (!this.AuthoritativeTick.IsValid)
      return;
    if (this.Engine._timer.IsFirstCall)
      this.Reconcile();
    if (this.Engine._config.AccurateLocalInterpolation && this.Engine._timer.IsLastCall)
      this.StateAllocator.CopyTo(this._interpFrom);
    this.CurrentInput = this.GetNewInput();
    this.InvokeNetworkFixedUpdate();
  }

  private void Reconcile()
  {
    this.LastAuthoritativeTick = this.AuthoritativeTick;
    int num = this.CurrentTick - this.AuthoritativeTick;
    this.CurrentTick = this.AuthoritativeTick;
    this.PredictedTick = this.PredictedTick.IsValid ? this.PredictedTick : this.CurrentTick;
    if (this.CurrentTick.IsValid && num >= this.Engine._config.MaxPredictedTicks)
    {
      this.Engine.Timer.OnHeavyLoss();
      this.RemoveAllInputs();
    }
    if (!this.Engine._config.AccurateLocalInterpolation)
    {
      this.From = this.CurrentTick.IsValid ? this._preRollback : this.From;
      this.StateAllocator.CopyTo(this._preRollback);
    }
    if (!this.IsLastTickFull || !this.CurrentTick.IsValid || num >= this.Engine._config.MaxPredictedTicks || this._lastAuth == null)
      return;
    this.RemoveAckedInputs(this.AuthoritativeTick - 1);
    this.PostReadPhase2();
    this.InvokePreRollback();
    this.StateAllocator.CopyTo(this.Previous);
    this.IsResimulating = true;
    if (this.IsLastTickFull != this.IsLastTickFullPrevious)
      this.HandleIncompleteSnapshotCase();
    this.PreInitializeSpawnedObjects();
    if (this.IsLastTickFull == this.IsLastTickFullPrevious)
      this.Rollback();
    else
      this.RollbackInterestedEntities();
    this.IsLastTickFullPrevious = this.IsLastTickFull;
    this.IsResimulating = false;
    Action postRollback = this.Engine.PostRollback;
    if (postRollback != null)
      postRollback();
    this.PostReadPhase3();
    this.Engine.Client.TryInvokeOnConnectedToServer();
    this.InitializeSpawnedObjects();
    this.UpdateIM();
    this.ApplyInputSourceData();
    this.InvokeOnInputSourceChanged();
    this.InvokeOnChangedForRollback();
    this.InvokePlayerJoinLeaveCallbacks();
    this.InvokeRPCs();
    this.Resimulate();
    this.InvokeOnChangedForMisprediction();
    this.InitializeSpawnedObjectsInterpolationBuffers();
    this.ReceivedEntities.Clear();
    this._addedEntities.Clear();
    this._changesCache.Clear();
  }

  private void Resimulate()
  {
    this.Engine._profiler.ResimulateProfiler?.Begin();
    this.IsResimulating = true;
    this.ResimulationStep = 0;
    this.ResimulationCount = 0;
    for (int index = 0; index < this.SavedInputs.Count; ++index)
    {
      if (!this.SavedInputs[index].Ignore)
        ++this.ResimulationCount;
    }
    this.Engine._profiler.Resimulations.Update((float) this.ResimulationCount);
    for (int index = 0; index < this.SavedInputs.Count; ++index)
    {
      this.CurrentInput = this.SavedInputs[index];
      if (!this.CurrentInput.Ignore)
      {
        Action networkFixedUpdate1 = this.Engine.PreNetworkFixedUpdate;
        if (networkFixedUpdate1 != null)
          networkFixedUpdate1();
        this.PredictedEntities.ExecuteNetworkFixedUpdate();
        Action networkFixedUpdate2 = this.Engine.PostNetworkFixedUpdate;
        if (networkFixedUpdate2 != null)
          networkFixedUpdate2();
      }
      ++this.CurrentTick;
      ++this.ResimulationStep;
      if (!this.CurrentInput.Ignore)
        this.PredictedEntities.ExecuteGameEngineIntoNetcode();
    }
    this.IsResimulating = false;
    this.Engine._profiler.ResimulateProfiler?.End();
    this.InvokePostResimulation();
  }

  private void Rollback()
  {
    this.Engine._profiler.NetcodeIntoGameEngineProfiler?.Begin();
    foreach (Entity receivedEntity in this.ReceivedEntities)
    {
      if (!receivedEntity.ChangeFlag)
      {
        this.ChangedEntities.Add(receivedEntity);
        receivedEntity.ChangeFlag = true;
      }
    }
    bool localInterpolation = this.Engine._config.AccurateLocalInterpolation;
    for (int index = 0; index < this.ChangedEntities.Count; ++index)
    {
      Entity changedEntity = this.ChangedEntities[index];
      changedEntity.ChangeFlag = false;
      if (changedEntity.HasFullState)
        this.RollbackEntity(changedEntity, localInterpolation);
    }
    for (int index = 0; index < this.ChangedEntities.Count; ++index)
    {
      if (this.ChangedEntities[index].HasFullState)
        this.ChangedEntities[index].InvokeNetcodeIntoGameEngine();
    }
    this.ChangedEntities.Clear();
    this.Engine._gameEngine.SyncTransformsToColliders();
    this.Engine._profiler.NetcodeIntoGameEngineProfiler?.End();
  }

  private void HandleIncompleteSnapshotCase()
  {
    foreach (Entity entity in this.Engine._IM.GlobalGroup._entities)
    {
      if (!entity.IsReady)
        this.ClientPendingAddedEntities.Add(entity);
    }
    foreach (InterestManagementGroup receivedGroup in this.ReceivedGroups)
    {
      foreach (Entity entity in receivedGroup._entities)
      {
        if (!entity.IsReady)
          this.ClientPendingAddedEntities.Add(entity);
      }
    }
  }

  private void RollbackInterestedEntities()
  {
    bool localInterpolation = this.Engine._config.AccurateLocalInterpolation;
    foreach (Entity entity in this.Engine._IM.GlobalGroup._entities)
      this.RollbackEntity(entity, localInterpolation);
    foreach (InterestManagementGroup receivedGroup in this.ReceivedGroups)
    {
      foreach (Entity entity in receivedGroup._entities)
        this.RollbackEntity(entity, localInterpolation);
    }
    foreach (Entity entity in this.Engine._IM.GlobalGroup._entities)
      entity.InvokeNetcodeIntoGameEngine();
    foreach (InterestManagementGroup receivedGroup in this.ReceivedGroups)
    {
      foreach (Entity entity in receivedGroup._entities)
        entity.InvokeNetcodeIntoGameEngine();
    }
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  private unsafe void RollbackEntity(Entity entity, bool copyCurrentStateToPreRollback)
  {
    OnChangedMethod[] propertyStartIndex = entity.ObjectMeta.PropertyElementsToPropertyStartIndex;
    int* numPtr = (int*) ((IntPtr) this._lastAuth.Blocks[entity.BlockIndex].Ptr + (IntPtr) entity.StateOffsetBytes);
    int* source = (int*) ((IntPtr) this.StateAllocator._allocator.Blocks[entity.BlockIndex].Ptr + (IntPtr) entity.StateOffsetBytes);
    int* dest = (int*) ((IntPtr) this._preRollback.Blocks[entity.BlockIndex].Ptr + (IntPtr) entity.StateOffsetBytes);
    long stateSizeWords = entity.StateSizeWords;
    if (copyCurrentStateToPreRollback)
      MemoryAllocation.Copy((void*) dest, (void*) source, stateSizeWords * 4L);
    for (int dataId = 0; (long) dataId < stateSizeWords; ++dataId)
    {
      if (source[dataId] != numPtr[dataId])
      {
        source[dataId] = numPtr[dataId];
        this.OnEntityStateChangedRemote(propertyStartIndex, entity, dataId);
      }
    }
  }

  public unsafe void PostReadPhase2()
  {
    EntityMetaReplicator entityMetaReplicator = this.Engine._entityMetaReplicator;
    bool interestManagement = this.Engine.Config.EnableInterestManagement;
    foreach (int changedElement in entityMetaReplicator.ChangedElements)
    {
      NetickEntityMeta meta = *entityMetaReplicator.GetEntityMeta(changedElement);
      Entity entity;
      if (!meta.IsDestroyed && this.AllEntities.TryGetValue(meta.Id, out entity))
      {
        entity.WorldIndex = changedElement;
        if (!entity.IsReady)
        {
          this.SetEntityInterestGroup(entity, meta);
          if (!interestManagement || this.Engine.InterestManagement.IsWithInClientAOI(entity, meta.GroupIndex))
            this.Engine._cliSim.ClientPendingAddedEntities.Add(entity);
        }
      }
    }
  }

  private unsafe void PostReadPhase3()
  {
    EntityMetaReplicator entityMetaReplicator = this.Engine._entityMetaReplicator;
    foreach (int changedElement in entityMetaReplicator.ChangedElements)
    {
      NetickEntityMeta netickMeta = *entityMetaReplicator.GetEntityMeta(changedElement);
      if (!netickMeta.IsDestroyed)
      {
        Entity entity;
        if (this.AllEntities.TryGetValue(netickMeta.Id, out entity))
          this.Engine._cliSim.ProcessEntityMetaInterest(entity, netickMeta);
        this.Engine._gameEngine.OnEntityMetaChangedPhase3(netickMeta, entityMetaReplicator.GetEntityUserMeta(changedElement), changedElement);
      }
    }
    foreach (int changedElement in entityMetaReplicator.ChangedElements)
    {
      NetickEntityMeta netickEntityMeta = *entityMetaReplicator.GetEntityMeta(changedElement);
      Entity entity;
      if (!netickEntityMeta.IsDestroyed && this.AllEntities.TryGetValue(netickEntityMeta.Id, out entity) && entity.IsReady)
        this.Engine._cliSim.ProcessEntityMetaInputSource(entity, netickEntityMeta);
    }
    entityMetaReplicator.ChangedElements.Clear();
  }

  private void UpdateIM()
  {
    if (!this.Engine.Config.EnableInterestManagement)
      return;
    this._preInterestList.Clear();
    foreach (InterestManagementGroup receivedGroup in this.ReceivedGroups)
    {
      if (!this._preInterestList.Contains(receivedGroup))
      {
        this._preInterestList.Add(receivedGroup);
        if (!this.InterestGroups.Contains(receivedGroup))
        {
          foreach (Entity entity in receivedGroup._entities)
          {
            if (entity.IsReady && !entity.NarrowPhaseFiltered && !entity._interestEnabled)
              this.OnBecameInterested(entity);
          }
        }
        this.InterestGroups.Remove(receivedGroup);
      }
    }
    foreach (InterestManagementGroup interestGroup in this.InterestGroups)
    {
      foreach (Entity entity in interestGroup._entities)
      {
        if (entity.IsReady && !entity.NarrowPhaseFiltered && entity._interestEnabled)
          this.OnBecameUninterested(entity);
      }
    }
    foreach (Entity entity in this.ReceivedEntitiesNarrow)
    {
      if (entity.RawId != -1 && entity.IsReady)
      {
        if (entity.NarrowPhaseFiltered)
        {
          if (entity._interestEnabled)
            this.OnBecameUninterested(entity);
        }
        else if (!entity._interestEnabled)
          this.OnBecameInterested(entity);
      }
    }
    this.ReceivedEntitiesNarrow.Clear();
    this.InterestGroups.Clear();
    foreach (InterestManagementGroup preInterest in this._preInterestList)
      this.InterestGroups.Add(preInterest);
  }

  internal void OnBecameInterested(Entity entity)
  {
    if (entity._inputSourceIndexForEvent != entity.InternalInputSourceIndex)
      this.InputSourceChangedEntities.Add(entity);
    entity._interestEnabled = true;
    entity.InvokeOnBecameInterested();
    if (!entity.UserEntity.AddToNetworkLoop)
      return;
    this.SimulatedEntities.AddToSimulation(entity);
  }

  internal void OnBecameUninterested(Entity entity)
  {
    entity._interestEnabled = false;
    entity.InvokeOnBecameUninterested();
    if (!entity.UserEntity.AddToNetworkLoop)
      return;
    this.SimulatedEntities.RemoveFromSimulation(entity);
  }

  private void SetIdsOnSpawnedObjects()
  {
    foreach (Entity pendingAddedEntity in this.ClientPendingAddedEntities)
      pendingAddedEntity.SafeId = pendingAddedEntity.RawId;
  }

  private unsafe void PreInitializeSpawnedObjects()
  {
    this._addedEntities.Clear();
    foreach (Entity pendingAddedEntity in this.ClientPendingAddedEntities)
    {
      NetickEntityMeta meta = *this.Engine._entityMetaReplicator.GetEntityMeta(pendingAddedEntity.WorldIndex);
      if (pendingAddedEntity.InternalInterestGroup == null)
        this.SetEntityInterestGroup(pendingAddedEntity, meta);
      if (!this.Engine.Config.EnableInterestManagement || this.Engine.InterestManagement.IsWithInClientAOI(pendingAddedEntity, pendingAddedEntity.InternalInterestGroup.Index))
      {
        pendingAddedEntity.SafeId = pendingAddedEntity.RawId;
        pendingAddedEntity.IsReady = true;
        this.ReceivedEntities.Add(pendingAddedEntity);
        this._addedEntities.Add(pendingAddedEntity);
        this.EntitiesList.Add(pendingAddedEntity);
        this.ActiveEntities.Add(pendingAddedEntity.SafeId, pendingAddedEntity);
        this.Engine._gameEngine.OnEntitySpawned(pendingAddedEntity);
      }
    }
    this.ClientPendingAddedEntities.Clear();
  }

  private unsafe void InitializeSpawnedObjects()
  {
    for (int index = 0; index < this._addedEntities.Count; ++index)
    {
      this.ProcessEntityMetaInputSource(this._addedEntities[index], *this.Engine._entityMetaReplicator.GetEntityMeta(this._addedEntities[index].WorldIndex));
      if (this._addedEntities[index].UserEntity.AddToNetworkLoop)
        this.SimulatedEntities.AddToSimulation(this._addedEntities[index]);
      this._networkStartExecutionList.Add(this._addedEntities[index]);
    }
    for (int index = 0; index < this._addedEntities.Count; ++index)
      this._addedEntities[index].UserEntity.NetworkAwake();
    for (int index = 0; index < this._addedEntities.Count; ++index)
      this._addedEntities[index].InvokeNetworkAwake();
    for (int index = 0; index < this._addedEntities.Count; ++index)
      this.ProcessEntityMetaInterest(this._addedEntities[index], *this.Engine._entityMetaReplicator.GetEntityMeta(this._addedEntities[index].WorldIndex));
    this._networkStartExecutionList.ExecuteNetworkStartAndClear();
  }

  private void InitializeSpawnedObjectsInterpolationBuffers()
  {
    for (int index = 0; index < this._addedEntities.Count; ++index)
      this.InitEntityInterpolationBuffers(this._addedEntities[index], this.StateAllocator._allocator);
  }

  internal void SetEntityInterestGroup(Entity entity, NetickEntityMeta meta)
  {
    if (this.Engine.Config.EnableInterestManagement && entity.UserEntity.BroadPhaseFilter != BroadPhaseFilter.Global)
    {
      this.Engine._IM.ClientSetEntityInterestGroup(entity, meta.GroupIndex);
    }
    else
    {
      this.Engine.InterestManagement.GlobalGroup._entities.Add(entity);
      entity.InternalInterestGroup = this.Engine.InterestManagement.GlobalGroup;
    }
  }

  internal void ProcessEntityMetaInterest(Entity entity, NetickEntityMeta item)
  {
    if (this.Engine.Config.EnableInterestManagement && entity.UserEntity.BroadPhaseFilter != BroadPhaseFilter.Global)
    {
      this.Engine._IM.ClientSetEntityInterestGroup(entity, item.GroupIndex);
    }
    else
    {
      this.Engine.InterestManagement.GlobalGroup._entities.Add(entity);
      entity.InternalInterestGroup = this.Engine.InterestManagement.GlobalGroup;
    }
    if (!this.Engine.Config.EnableInterestManagement)
      return;
    bool flag = this.Engine.InterestManagement.IsWithInClientAOI(entity, item.GroupIndex);
    if (!flag)
    {
      if (entity._interestEnabled)
        this.OnBecameUninterested(entity);
    }
    else if (!entity._interestEnabled)
      this.OnBecameInterested(entity);
    entity._interestEnabled = flag;
  }

  internal void ProcessEntityMetaInputSource(Entity entity, NetickEntityMeta item)
  {
    bool flag1 = this.LocalInputSourceIndex == item.InputSourceId;
    bool flag2 = entity._inputSourceIndexForEvent == this.LocalInputSourceIndex;
    entity.InternalInputSourceIndex = item.InputSourceId;
    if (flag1 && !flag2)
    {
      this.InputSourceChangedEntities.Add(entity);
    }
    else
    {
      if (!(!flag1 & flag2))
        return;
      this.InputSourceChangedEntities.Add(entity);
    }
  }

  private void ApplyEntityMetaInputSource(Entity entity, NetickEntityMeta item)
  {
    if (entity.RawId == -1 || !entity._interestEnabled)
      return;
    bool flag1 = this.LocalInputSourceIndex == item.InputSourceId;
    bool flag2 = entity._inputSourceIndexForEvent == this.LocalInputSourceIndex;
    if (flag1 && !flag2)
    {
      this.PendingClientInputSourceChangedEvents.Enqueue(new Simulation.InputSourceChangedEvent()
      {
        Entity = entity,
        PreviousPlayer = (NetworkPlayer) null
      });
      if (!entity._isOnPredictedList)
      {
        if (entity.UserEntity.AddToNetworkLoop)
          this.PredictedEntities.Add(entity);
        entity._isOnPredictedList = true;
      }
    }
    else if (!flag1 & flag2)
    {
      this.PendingClientInputSourceChangedEvents.Enqueue(new Simulation.InputSourceChangedEvent()
      {
        Entity = entity,
        PreviousPlayer = (NetworkPlayer) this.Engine.LocalPlayer
      });
      if (entity._isOnPredictedList)
      {
        if (entity.UserEntity.AddToNetworkLoop)
          this.PredictedEntities.Remove(entity);
        entity._isOnPredictedList = false;
      }
    }
    entity._inputSourceIndexForEvent = item.InputSourceId;
    entity.InternalInputSourceIndex = item.InputSourceId;
  }

  private unsafe void ApplyInputSourceData()
  {
    foreach (Entity sourceChangedEntity in this.InputSourceChangedEntities)
      this.ApplyEntityMetaInputSource(sourceChangedEntity, *this.Engine._entityMetaReplicator.GetEntityMeta(sourceChangedEntity.WorldIndex));
  }

  private void InvokeOnInputSourceChanged()
  {
    this.InputSourceChangedEntities.Clear();
    while (this.PendingClientInputSourceChangedEvents.Count > 0)
    {
      Simulation.InputSourceChangedEvent sourceChangedEvent = this.PendingClientInputSourceChangedEvents.Dequeue();
      try
      {
        sourceChangedEvent.Entity.InvokeOnInputSourceChanged(sourceChangedEvent.PreviousPlayer);
      }
      catch (Exception ex)
      {
        NetickLogger.LogError(this.Engine, (object) ex);
      }
    }
  }

  private SimulationInput GetNewInput()
  {
    if (!this.CurrentTick.IsValid || this.CurrentTick - this.AuthoritativeTick >= this.Engine._config.MaxPredictedTicks)
      return (SimulationInput) null;
    SimulationInput input = this.CreateInput(this.Engine._remoteInterpolation.To, this.CurrentTick, this.Engine._remoteInterpolation.Alpha);
    this.SavedInputs.Add(input);
    while (this.SavedInputs.Count >= this.Engine._config.MaxPredictedTicks)
    {
      this.RecycleInput(this.SavedInputs[0]);
      this.SavedInputs.RemoveAt(0);
    }
    return input;
  }

  private void RemoveAckedInputs(Tick tick)
  {
    while (this.SavedInputs.Count > 0 && this.SavedInputs[0].TargetTick <= tick)
    {
      this.RecycleInput(this.SavedInputs[0]);
      this.SavedInputs.RemoveAt(0);
    }
  }

  private void RemoveAllInputs()
  {
    for (int index = 0; index < this.SavedInputs.Count; ++index)
      this.RecycleInput(this.SavedInputs[index]);
    this.SavedInputs.Clear();
  }

  internal void OnFullSnapshot(Tick simulationTick, bool simReady)
  {
    this.SimulationReady = simReady;
    this.AuthoritativeTick = simulationTick;
    this._lastAuth = this._snapshots[simulationTick % this._snapshots.Length];
    this._recvBuffer.CopyTo(this._snapshots[simulationTick % this._snapshots.Length]);
    this.Engine._remoteInterpolation.AddSnapshot(simulationTick, this.Engine._cliSim._recvBuffer);
    this.Engine._adaptiveReplicator?.AddSnapshots();
  }

  internal unsafe void ReadMeta(BitBuffer buffer, bool didWriteAll)
  {
    int num1 = 0;
    int dataIndex = 0;
    int* statePtr = this.Engine._internalReplicator.Entity.StatePtr;
    int num2 = (int) buffer.Read(1);
    int sizeWords = this.Engine._entityMetaReplicator.GetSizeWords();
    while (true)
    {
      int num3 = buffer.ReadInt4();
      if ((num1 != 0 || num3 != -1) && (num1 <= 0 || num3 != 0))
      {
        dataIndex = num3 + dataIndex;
        statePtr[dataIndex] = buffer.ReadInt();
        if (dataIndex < sizeWords)
          this.Engine._entityMetaReplicator.OnElementRead(dataIndex);
        ++num1;
      }
      else
        break;
    }
    if (!didWriteAll)
      return;
    this.Engine._internalReplicator.PostRead();
  }

  internal void ReadGroups(
    BitBuffer buff,
    uint writtenGroupCount,
    bool didWriteAll,
    Tick simulationTick,
    NetickAllocator basisSnapshot)
  {
    if (!didWriteAll)
      this.ReceivedEntities.Clear();
    bool interestManagement = this.Engine._config.EnableInterestManagement;
    for (int index = 0; (long) index < (long) writtenGroupCount; ++index)
      this.ReadEntities(buff, interestManagement, didWriteAll, simulationTick, basisSnapshot);
  }

  internal void ReadEntities(
    BitBuffer buff,
    bool isIMEnabled,
    bool didWriteAll,
    Tick simulationTick,
    NetickAllocator basisSnapshot)
  {
    int num1 = 0;
    uint num2 = buff.Read(20);
    buff.AlignReadPosToByte();
    for (int index = 0; (long) index < (long) num2; ++index)
    {
      Entity entity = (Entity) null;
      int key = buff.ReadInt4() + num1;
      bool isUnlinked = false;
      if (!this.AllEntities.TryGetValue(key, out entity))
      {
        isUnlinked = true;
        entity = this.UnlinkedEntities[key];
      }
      num1 = key;
      if (entity.NarrowInterests != null)
      {
        bool flag = buff.ReadBool();
        entity.NarrowPhaseFiltered = !flag;
        if (!isUnlinked)
          this.ReceivedEntitiesNarrow.Add(entity);
        if (!flag)
          continue;
      }
      this.ReadEntity(buff, basisSnapshot, entity, didWriteAll, simulationTick, isUnlinked);
      if (!isUnlinked && didWriteAll)
      {
        this.ReceivedEntities.Add(entity);
        if (!entity.IsReady)
          this.ClientPendingAddedEntities.Add(entity);
      }
    }
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  private unsafe void ReadEntity(
    BitBuffer buffer,
    NetickAllocator basisSnap,
    Entity entity,
    bool didWriteAll,
    Tick simTick,
    bool isUnlinked)
  {
    entity.HasFullState = didWriteAll;
    int* statePtr = entity.StatePtr;
    int* numPtr1 = (int*) ((IntPtr) this._recvBuffer.Blocks[entity.BlockIndex].Ptr + (IntPtr) entity.StateOffsetBytes);
    int* numPtr2 = (int*) ((IntPtr) this._recvMap.Blocks[entity.BlockIndex].Ptr + (IntPtr) entity.StateOffsetBytes);
    int* numPtr3 = basisSnap == null ? (int*) null : (int*) ((IntPtr) basisSnap.Blocks[entity.BlockIndex].Ptr + (IntPtr) entity.StateOffsetBytes);
    int index = 0;
    int num1 = 0;
    basisSnap = buffer.Read(1) == 1U ? basisSnap : (NetickAllocator) null;
    long num2 = entity.StateSizeWords - 1L;
    while (true)
    {
      int num3 = buffer.ReadInt4();
      if ((num1 != 0 || num3 != -1) && (num1 <= 0 || num3 != 0))
      {
        index = num3 + index;
        if (index >= 0 && (long) index <= num2)
        {
          int num4 = basisSnap != null ? buffer.ReadInt4() + numPtr3[index] : buffer.ReadInt();
          numPtr1[index] = num4;
          numPtr2[index] = 1;
          ++num1;
        }
        else
          goto label_3;
      }
      else
        break;
    }
    return;
label_3:
    throw new BadPacketException("Server sent bad data.");
  }
}
